using System;
using System.Data;
using System.Transactions;
using Oracle.ManagedDataAccess.Client;
using OracleInternal.Common;
using OracleInternal.ConnectionPool;
using OracleInternal.ServiceObjects;

namespace OracleInternal.MTS
{
	internal abstract class MTSTxnBranch
	{
		internal const int ORA_ERR_SET_TXN_ISOLATIONLVL = 1453;

		protected MTSTxnRM m_mtsTxnRM;

		internal string m_txnLocalID = string.Empty;

		protected TransXID m_xid;

		internal OpoDTCTxnCtx m_opoDTCTxnCtx;

		internal ConnectionString m_connCreds;

		protected int m_branchNum = -1;

		internal bool m_bNew = true;

		internal System.Data.IsolationLevel m_txnIsolationLvl = System.Data.IsolationLevel.ReadCommitted;

		internal string m_dbInstance = string.Empty;

		protected TxnBranchState m_branchState;

		private object m_lock = new object();

		internal int BranchNumber => m_branchNum;

		internal TransXID TxnID => m_xid;

		internal TxnBranchState State => m_branchState;

		internal bool IsInTxn
		{
			get
			{
				if (m_branchState != TxnBranchState.InUse)
				{
					return m_branchState == TxnBranchState.Free;
				}
				return true;
			}
		}

		internal static MTSTxnBranch CreateTxnBranch(bool bIsCCP, MTSTxnRM txnRM, int branchNum)
		{
			return new CCPMTSTxnBranch(txnRM, branchNum);
		}

		internal MTSTxnBranch(MTSTxnRM txnRM, int branchNum)
		{
			m_mtsTxnRM = txnRM;
			m_branchNum = branchNum;
		}

		~MTSTxnBranch()
		{
		}

		internal abstract OracleConnectionImpl GetConnection(bool bMustMatch, out bool bMatchFound);

		internal abstract bool CanResetConnection(bool bMatchConn, TxnBranchState branchState);

		internal virtual void ReleaseConnection(string txnOperation, OracleConnectionImpl connImpl, TransXID txnXID)
		{
		}

		internal virtual void SetConnection(OracleConnectionImpl connImpl)
		{
		}

		internal void Set(string txnLocalID, Guid txnXID, System.Transactions.IsolationLevel txnIsolationLvl)
		{
			m_txnLocalID = txnLocalID;
			m_xid = TransXID.CreateOracleXID(txnXID, m_mtsTxnRM.m_RMGuid, m_branchNum);
			m_opoDTCTxnCtx = new OpoDTCTxnCtx(m_xid.m_opoDTCTxnXID);
			if (!ConfigBaseClass.m_bLegacyIsolationLevelBehavior && txnIsolationLvl == System.Transactions.IsolationLevel.Serializable)
			{
				m_txnIsolationLvl = System.Data.IsolationLevel.Serializable;
			}
		}

		internal void StartDistributedTransaction(OracleConnectionImpl connImpl)
		{
			if (ProviderConfig.m_bTraceLevelPrivate)
			{
				Trace.Write(OracleTraceLevel.Private, (OracleTraceTag)4352, OracleTraceClassName.MTSTxnBranch, OracleTraceFuncName.StartDistributedTransaction, "Branch State = " + m_branchState.ToString() + "\t" + (m_bNew ? "Start " : "Resume ") + " TxnID = " + m_xid?.ToString() + " using Conn ID = " + connImpl.m_endUserSessionId + " to DBInst = " + connImpl.m_instanceName + " Isolation Level = " + m_txnIsolationLvl);
			}
			try
			{
				if (connImpl.m_currentIsolationLvl != m_txnIsolationLvl && !m_mtsTxnRM.m_bIgnoreIsolationLvl)
				{
					connImpl.SwitchIsolationLevel(m_txnIsolationLvl);
				}
				if (m_bNew)
				{
					MTSTransactionImpl.Start(connImpl, m_opoDTCTxnCtx, ConfigBaseClass.m_dtcTxnTimeout);
				}
				else
				{
					MTSTransactionImpl.Resume(connImpl, m_opoDTCTxnCtx, ConfigBaseClass.m_dtcTxnTimeout);
				}
				m_mtsTxnRM.m_bIgnoreIsolationLvl = true;
				m_connCreds = connImpl.m_cs;
				connImpl.SetAutoCommit(bAutoCommitOn: false);
				if (connImpl.m_mtsTxnCtx == null)
				{
					connImpl.m_mtsTxnCtx = MTSTxnCtx.CreateMTSTxnCtx(connImpl);
				}
				connImpl.m_mtsTxnCtx.SetDistributedCtx(m_txnLocalID, this, connImpl.m_endUserSessionId);
				SetConnection(connImpl);
				m_dbInstance = connImpl.m_instanceName;
				m_branchState = TxnBranchState.InUse;
			}
			catch (Exception ex)
			{
				if (ProviderConfig.m_bTraceLevelPrivate)
				{
					Trace.Write(OracleTraceLevel.Private, (OracleTraceTag)268439552, OracleTraceClassName.MTSTxnBranch, OracleTraceFuncName.StartDistributedTransaction, "Error in starting transaction " + (m_bNew ? "Start " : "Resume ") + " TxnID = " + m_xid?.ToString() + " using Conn ID = " + connImpl.m_endUserSessionId + " to DBInst = " + connImpl.m_instanceName + Environment.NewLine + ex.Message);
				}
				throw;
			}
			finally
			{
				if (ProviderConfig.m_bTraceLevelPrivate)
				{
					Trace.Write(OracleTraceLevel.Private, (OracleTraceTag)4608, OracleTraceClassName.MTSTxnBranch, OracleTraceFuncName.StartDistributedTransaction, "Branch State = " + m_branchState.ToString() + "\t" + (m_bNew ? "Start " : "Resume ") + " TxnID = " + m_xid?.ToString() + " using Conn ID = " + connImpl.m_endUserSessionId + " to DBInst = " + connImpl.m_instanceName);
				}
			}
		}

		internal void PromoteDistributedTransaction(OracleConnectionImpl connImpl)
		{
			if (ProviderConfig.m_bTraceLevelPrivate)
			{
				Trace.Write(OracleTraceLevel.Private, (OracleTraceTag)4352, OracleTraceClassName.MTSTxnBranch, OracleTraceFuncName.PromoteDistributedTransaction, "Branch State = " + m_branchState.ToString() + "\t TxnID = " + m_xid?.ToString() + " using Conn ID = " + connImpl.m_endUserSessionId + " to DBInst = " + connImpl.m_instanceName);
			}
			try
			{
				MTSTransactionImpl.Promote(connImpl, m_opoDTCTxnCtx, ConfigBaseClass.m_dtcTxnTimeout);
				m_connCreds = connImpl.m_cs;
				connImpl.SetAutoCommit(bAutoCommitOn: false);
				connImpl.m_mtsTxnCtx.SetDistributedCtx(m_txnLocalID, this, connImpl.m_endUserSessionId);
				SetConnection(connImpl);
				m_dbInstance = connImpl.m_instanceName;
				m_branchState = TxnBranchState.InUse;
			}
			finally
			{
				if (ProviderConfig.m_bTraceLevelPrivate)
				{
					Trace.Write(OracleTraceLevel.Private, (OracleTraceTag)4608, OracleTraceClassName.MTSTxnBranch, OracleTraceFuncName.PromoteDistributedTransaction, "Branch State = " + m_branchState.ToString() + "\tTxnID = " + m_xid?.ToString() + " using Conn ID = " + connImpl.m_endUserSessionId + " to DBInst = " + connImpl.m_instanceName);
				}
			}
		}

		internal void Detach()
		{
			if (ProviderConfig.m_bTraceLevelPrivate)
			{
				Trace.Write(OracleTraceLevel.Private, (OracleTraceTag)4352, OracleTraceClassName.MTSTxnBranch, OracleTraceFuncName.Detach, "Branch State = " + m_branchState.ToString() + "\t TxnID = " + m_xid);
			}
			OracleConnectionImpl oracleConnectionImpl = null;
			bool bMatchFound = false;
			try
			{
				if (m_branchState != TxnBranchState.InUse || m_branchState != TxnBranchState.InUse)
				{
					return;
				}
				lock (m_lock)
				{
					if (m_branchState != TxnBranchState.InUse)
					{
						return;
					}
					oracleConnectionImpl = GetConnection(bMustMatch: true, out bMatchFound);
					if (oracleConnectionImpl == null || !oracleConnectionImpl.m_bConnected)
					{
						return;
					}
					MTSTransactionImpl.Detach(oracleConnectionImpl, m_opoDTCTxnCtx, ConfigBaseClass.m_dtcTxnTimeout);
					if (bMatchFound)
					{
						oracleConnectionImpl.SetAutoCommit(bAutoCommitOn: true);
						if (oracleConnectionImpl.m_mtsTxnCtx != null)
						{
							oracleConnectionImpl.ResetMTSTxnCtx();
						}
					}
					m_branchState = TxnBranchState.Free;
					m_mtsTxnRM.FreeTxnBranch(this);
				}
			}
			catch (Exception ex)
			{
				OracleException.HandleError(OracleTraceLevel.Private, OracleTraceTag.MTS, OracleTraceClassName.MTSTxnBranch, OracleTraceFuncName.Detach, ex);
			}
			finally
			{
				if (oracleConnectionImpl != null)
				{
					ReleaseConnection("Detach", oracleConnectionImpl, m_xid);
				}
				if (ProviderConfig.m_bTraceLevelPrivate)
				{
					Trace.Write(OracleTraceLevel.Private, (OracleTraceTag)4608, OracleTraceClassName.MTSTxnBranch, OracleTraceFuncName.Detach, "Branch State = " + m_branchState.ToString() + "\t TxnID = " + m_xid);
				}
			}
		}

		internal void Detach(OracleConnectionImpl connImpl)
		{
			if (ProviderConfig.m_bTraceLevelPrivate)
			{
				Trace.Write(OracleTraceLevel.Private, (OracleTraceTag)4352, OracleTraceClassName.MTSTxnBranch, OracleTraceFuncName.Detach, "Branch State = " + m_branchState.ToString() + "\t TxnID = " + m_xid?.ToString() + " using Conn ID = " + connImpl.m_endUserSessionId + " to DBInst = " + connImpl.m_instanceName);
			}
			try
			{
				if (m_branchState == TxnBranchState.InUse && connImpl.m_bConnected)
				{
					MTSTransactionImpl.Detach(connImpl, m_opoDTCTxnCtx, ConfigBaseClass.m_dtcTxnTimeout);
				}
			}
			catch (Exception ex)
			{
				OracleException.HandleError(OracleTraceLevel.Private, OracleTraceTag.MTS, OracleTraceClassName.MTSTxnBranch, OracleTraceFuncName.Detach, ex);
			}
			finally
			{
				if (connImpl.m_bConnected)
				{
					connImpl.SetAutoCommit(bAutoCommitOn: true);
					if (connImpl.m_mtsTxnCtx != null)
					{
						connImpl.ResetMTSTxnCtx();
					}
					m_branchState = TxnBranchState.Free;
					m_mtsTxnRM.FreeTxnBranch(this);
				}
				if (ProviderConfig.m_bTraceLevelPrivate)
				{
					Trace.Write(OracleTraceLevel.Private, (OracleTraceTag)4608, OracleTraceClassName.MTSTxnBranch, OracleTraceFuncName.Detach, "Branch State = " + m_branchState.ToString() + "\t TxnID = " + m_xid?.ToString() + " using Conn ID = " + connImpl.m_endUserSessionId + " to DBInst = " + connImpl.m_instanceName);
				}
			}
		}

		internal TxnState Prepare()
		{
			if (ProviderConfig.m_bTraceLevelPrivate)
			{
				Trace.Write(OracleTraceLevel.Private, (OracleTraceTag)4352, OracleTraceClassName.MTSTxnBranch, OracleTraceFuncName.Prepare, "Branch State = " + m_branchState.ToString() + "\t TxnID = " + m_xid?.ToString() + " Branch # = " + m_branchNum + "\t Local Txn ID = " + m_txnLocalID);
			}
			try
			{
				if (m_branchState == TxnBranchState.Done || m_branchState == TxnBranchState.NotValid)
				{
					return TxnState.K2CMDrdonly;
				}
				TxnState txnState = TxnState.Error;
				OracleConnectionImpl oracleConnectionImpl = null;
				bool bMatchFound = false;
				try
				{
					if (m_branchState == TxnBranchState.InUse || m_branchState == TxnBranchState.Free)
					{
						lock (m_lock)
						{
							if (m_branchState == TxnBranchState.InUse || m_branchState == TxnBranchState.Free)
							{
								oracleConnectionImpl = GetConnection(bMustMatch: false, out bMatchFound);
								if (oracleConnectionImpl.m_bConnected)
								{
									txnState = MTSTransactionImpl.Prepare(oracleConnectionImpl, m_opoDTCTxnCtx, ConfigBaseClass.m_dtcTxnTimeout, bMatchFound);
									if (ProviderConfig.m_bTraceLevelPrivate)
									{
										Trace.Write(OracleTraceLevel.Private, OracleTraceTag.MTS, OracleTraceClassName.MTSTxnBranch, OracleTraceFuncName.Prepare, "MTSTxnBranch.Prepare(): Prepare State = " + txnState.ToString() + "\tTxnID = " + m_xid?.ToString() + " using Conn ID = " + oracleConnectionImpl.m_endUserSessionId + " to DBInst = " + oracleConnectionImpl.m_instanceName);
									}
									if (txnState == TxnState.K2CMDrdonly || txnState == TxnState.K2CMDrqcommit)
									{
										if (txnState == TxnState.K2CMDrdonly)
										{
											m_branchState = TxnBranchState.Done;
										}
										else
										{
											m_branchState = TxnBranchState.Free;
										}
										if (CanResetConnection(bMatchFound, m_branchState))
										{
											oracleConnectionImpl.SetAutoCommit(bAutoCommitOn: true);
											if (oracleConnectionImpl.m_mtsTxnCtx != null)
											{
												oracleConnectionImpl.ResetMTSTxnCtx();
											}
										}
									}
								}
							}
						}
					}
				}
				catch (Exception ex)
				{
					OracleException.HandleError(OracleTraceLevel.Private, OracleTraceTag.MTS, OracleTraceClassName.MTSTxnBranch, OracleTraceFuncName.Prepare, ex);
					txnState = TxnState.Error;
					throw;
				}
				finally
				{
					if (oracleConnectionImpl != null)
					{
						ReleaseConnection("Prepare", oracleConnectionImpl, m_xid);
					}
				}
				return txnState;
			}
			finally
			{
				if (ProviderConfig.m_bTraceLevelPrivate)
				{
					Trace.Write(OracleTraceLevel.Private, (OracleTraceTag)4608, OracleTraceClassName.MTSTxnBranch, OracleTraceFuncName.Prepare, "Branch State = " + m_branchState.ToString() + "\t TxnID = " + m_xid?.ToString() + " Branch # = " + m_branchNum + "\t Local Txn ID = " + m_txnLocalID);
				}
			}
		}

		internal TxnState Commit()
		{
			if (ProviderConfig.m_bTraceLevelPrivate)
			{
				Trace.Write(OracleTraceLevel.Private, (OracleTraceTag)4352, OracleTraceClassName.MTSTxnBranch, OracleTraceFuncName.Commit, "Branch State = " + m_branchState.ToString() + "\t TxnID = " + m_xid);
			}
			try
			{
				if (m_branchState == TxnBranchState.Done || m_branchState == TxnBranchState.NotValid)
				{
					return TxnState.K2CMDcommit;
				}
				TxnState txnState = TxnState.Error;
				OracleConnectionImpl oracleConnectionImpl = null;
				bool bMatchFound = false;
				try
				{
					if (m_branchState == TxnBranchState.InUse || m_branchState == TxnBranchState.Free)
					{
						lock (m_lock)
						{
							if (m_branchState == TxnBranchState.InUse || m_branchState == TxnBranchState.Free)
							{
								oracleConnectionImpl = GetConnection(bMustMatch: false, out bMatchFound);
								txnState = MTSTransactionImpl.Commit(oracleConnectionImpl, m_opoDTCTxnCtx, ConfigBaseClass.m_dtcTxnTimeout, bMatchFound);
								if (ProviderConfig.m_bTraceLevelPrivate)
								{
									Trace.Write(OracleTraceLevel.Private, OracleTraceTag.MTS, OracleTraceClassName.MTSTxnBranch, OracleTraceFuncName.Commit, "MTSTxnBranch.Commit(): Commit State = " + txnState.ToString() + "\tTxnID = " + m_xid?.ToString() + " using Conn ID = " + oracleConnectionImpl.m_endUserSessionId + " to DBInst = " + oracleConnectionImpl.m_instanceName);
								}
								if (txnState == TxnState.K2CMDcommit || txnState == TxnState.K2CMDforget)
								{
									m_branchState = TxnBranchState.Done;
									if (bMatchFound)
									{
										oracleConnectionImpl.SetAutoCommit(bAutoCommitOn: true);
										if (oracleConnectionImpl.m_mtsTxnCtx != null)
										{
											oracleConnectionImpl.ResetMTSTxnCtx();
										}
									}
								}
							}
						}
					}
				}
				catch (Exception ex)
				{
					OracleException.HandleError(OracleTraceLevel.Private, OracleTraceTag.MTS, OracleTraceClassName.MTSTxnBranch, OracleTraceFuncName.Commit, ex);
					txnState = TxnState.Error;
					throw;
				}
				finally
				{
					if (oracleConnectionImpl != null)
					{
						ReleaseConnection("Commit", oracleConnectionImpl, m_xid);
					}
				}
				return txnState;
			}
			finally
			{
				if (ProviderConfig.m_bTraceLevelPrivate)
				{
					Trace.Write(OracleTraceLevel.Private, (OracleTraceTag)4608, OracleTraceClassName.MTSTxnBranch, OracleTraceFuncName.Commit, "Branch State = " + m_branchState.ToString() + "\t TxnID = " + m_xid);
				}
			}
		}

		internal TxnState Abort()
		{
			if (ProviderConfig.m_bTraceLevelPrivate)
			{
				Trace.Write(OracleTraceLevel.Private, (OracleTraceTag)4352, OracleTraceClassName.MTSTxnBranch, OracleTraceFuncName.Abort, "Branch State = " + m_branchState.ToString() + "\t TxnID = " + m_xid);
			}
			try
			{
				if (m_branchState == TxnBranchState.Done || m_branchState == TxnBranchState.NotValid)
				{
					return TxnState.K2CMDabort;
				}
				TxnState txnState = TxnState.Error;
				OracleConnectionImpl oracleConnectionImpl = null;
				bool bMatchFound = false;
				try
				{
					if (m_branchState == TxnBranchState.InUse || m_branchState == TxnBranchState.Free)
					{
						lock (m_lock)
						{
							if (m_branchState == TxnBranchState.InUse || m_branchState == TxnBranchState.Free)
							{
								oracleConnectionImpl = GetConnection(bMustMatch: false, out bMatchFound);
								txnState = MTSTransactionImpl.Abort(oracleConnectionImpl, m_opoDTCTxnCtx, 2u, bMatchFound);
								if (ProviderConfig.m_bTraceLevelPrivate)
								{
									Trace.Write(OracleTraceLevel.Private, OracleTraceTag.MTS, OracleTraceClassName.MTSTxnBranch, OracleTraceFuncName.Abort, "Abort State = " + txnState.ToString() + "\tTxnID = " + m_xid?.ToString() + " using Conn ID = " + oracleConnectionImpl.m_endUserSessionId + " to DBInst = " + oracleConnectionImpl.m_instanceName);
								}
								if (txnState == TxnState.K2CMDabort)
								{
									m_branchState = TxnBranchState.Done;
									if (bMatchFound)
									{
										oracleConnectionImpl.SetAutoCommit(bAutoCommitOn: true);
										if (oracleConnectionImpl.m_mtsTxnCtx != null)
										{
											oracleConnectionImpl.ResetMTSTxnCtx();
										}
									}
								}
							}
						}
					}
				}
				catch (Exception ex)
				{
					OracleException.HandleError(OracleTraceLevel.Private, OracleTraceTag.MTS, OracleTraceClassName.MTSTxnBranch, OracleTraceFuncName.Abort, ex);
					txnState = TxnState.Error;
					throw;
				}
				finally
				{
					if (oracleConnectionImpl != null)
					{
						ReleaseConnection("Abort", oracleConnectionImpl, m_xid);
					}
				}
				return txnState;
			}
			finally
			{
				if (ProviderConfig.m_bTraceLevelPrivate)
				{
					Trace.Write(OracleTraceLevel.Private, (OracleTraceTag)4608, OracleTraceClassName.MTSTxnBranch, OracleTraceFuncName.Abort, "Branch State = " + m_branchState.ToString() + "\t TxnID = " + m_xid);
				}
			}
		}
	}
}
